﻿using CoachingOrganizer.ClientInfrastructure.ServicesEntities.LocalStorage.Tools.MoneyCalculation;
using CoachingOrganizer.Logic.Helpers;
using CoachingOrganizer.Logic.Logic.Tools.MoneyCalculation;
using CoachingOrganizer.Toolkit.Events;
using CoachingOrganizer.Toolkit.Threading.CollectionsDecorators;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoachingOrganizer.Logic.Models.Tools.MoneyCalculation
{
    public abstract class MoneyFlowModel
    {
        private readonly SimpleCollectionLoadDecorator<StableMoneyFlowItemModel> _stableItems;
        private readonly SimpleCollectionLoadDecorator<OccasionalMoneyFlowItemModel> _occasionalItems;
        private string _newStableMoneyFlowItemName;
        private string _newOccasionalMoneyFlowItemName;

        public abstract int Sign { get; }

        public IEnumerable<StableMoneyFlowItemModel> StableItems
        {
            get { return _stableItems.Items; }
        }

        public IEnumerable<OccasionalMoneyFlowItemModel> OccasionalItems
        {
            get { return _occasionalItems.Items; }
        }

        public event NotifyCollectionChangedEventHandler StableMoneyFlowItemsCollectionChanged
        {
            add { _stableItems.ItemsCollectionChanged += value; }
            remove { _stableItems.ItemsCollectionChanged -= value; }
        }

        public event NotifyCollectionChangedEventHandler OccasionalMoneyFlowItemsCollectionChanged
        {
            add { _occasionalItems.ItemsCollectionChanged += value; }
            remove { _occasionalItems.ItemsCollectionChanged -= value; }
        }

        public string NewStableMoneyFlowItemName
        {
            get { return _newStableMoneyFlowItemName; }
            set
            {
                _newStableMoneyFlowItemName = value;
                OnNewStableMoneyFlowItemNameChanged();
            }
        }

        public string NewOccasionalMoneyFlowItemName
        {
            get { return _newOccasionalMoneyFlowItemName; }
            set
            {
                _newOccasionalMoneyFlowItemName = value;
                OnNewOccasionalMoneyFlowItemNameChanged();
            }
        }

        public event EventHandler NewStableMoneyFlowItemNameChanged;

        public event EventHandler NewOccasionalMoneyFlowItemNameChanged;

        internal Func<ActionResult<IEnumerable<StableMoneyFlowItemLogic>>> EnumerateStableMoneyFlowItemsDelegate;

        internal Func<ActionResult<IEnumerable<OccasionalMoneyFlowItemLogic>>> EnumerateOccasionalMoneyFlowItemsDelegate;

        internal Func<Action<IStableMoneyFlowItemInfo>, ActionResult<StableMoneyFlowItemLogic>> AddNewStableMoneyFlowItemDelegate;

        internal Func<Action<IOccasionalMoneyFlowItemInfo>, ActionResult<OccasionalMoneyFlowItemLogic>> AddNewOccasionalMoneyFlowItemDelegate;

        internal Func<IEnumerable<MoneyFlowItemNameSuggestionLogic>> EnumerateStableItemsNameSuggestionsDelegate;

        internal Func<IEnumerable<MoneyFlowItemNameSuggestionLogic>> EnumerateOccasionalItemsNameSuggestionsDelegate;

        public MoneyFlowModel()
        {
            _stableItems = new SimpleCollectionLoadDecorator<StableMoneyFlowItemModel>(EnumerateStableMoneyFlowItems);
            _occasionalItems = new SimpleCollectionLoadDecorator<OccasionalMoneyFlowItemModel>(EnumerateOccasionalMoneyFlowItems);
        }

        public ActionResult<IEnumerable<MoneyFlowItemNameSuggestionModel>> EnumerateStableItemsNameSuggestions()
        {
            Func<IEnumerable<MoneyFlowItemNameSuggestionLogic>> requestDelegate = EnumerateStableItemsNameSuggestionsDelegate;
            return requestDelegate == null ? ActionResult<IEnumerable<MoneyFlowItemNameSuggestionModel>>.GetErrorResult(new NotSupportedException()) : ActionResult<IEnumerable<MoneyFlowItemNameSuggestionModel>>.GetValidResult(requestDelegate().Select(l => l.Model));
        }

        public ActionResult<IEnumerable<MoneyFlowItemNameSuggestionModel>> EnumerateOccasionalItemsNameSuggestions()
        {
            Func<IEnumerable<MoneyFlowItemNameSuggestionLogic>> requestDelegate = EnumerateOccasionalItemsNameSuggestionsDelegate;
            return requestDelegate == null ? ActionResult<IEnumerable<MoneyFlowItemNameSuggestionModel>>.GetErrorResult(new NotSupportedException()) : ActionResult<IEnumerable<MoneyFlowItemNameSuggestionModel>>.GetValidResult(requestDelegate().Select(l => l.Model));
        }

        public async Task LoadMoneyFlowItemsAsync(System.Threading.CancellationToken cancelLoad)
        {
            await Task.Run(() =>
            {
                _stableItems.FillItemsList();
                _occasionalItems.FillItemsList();
            }, cancelLoad);
        }

        public async Task<ActionResult> AddNewStableMoneyFlowItem(string name)
        {
            return await Task.Run(() =>
            {
                ActionResult<StableMoneyFlowItemModel> newMoneyFlowItemResult = UpdateHelper.GetModel(AddNewStableMoneyFlowItemDelegate, d => d.Name = name, l => l.Model);
                if (newMoneyFlowItemResult.IsValid)
                    _stableItems.ModifyCollection(col => col.Add(newMoneyFlowItemResult.Result));
                return newMoneyFlowItemResult.IsValid ? ActionResult.ValidResult : ActionResult.GetErrorResult(newMoneyFlowItemResult);
            });
        }

        public async Task<ActionResult> AddNewOccasionalMoneyFlowItem(string name)
        {
            return await Task.Run(() =>
            {
                ActionResult<OccasionalMoneyFlowItemModel> newMoneyFlowItemResult = UpdateHelper.GetModel(AddNewOccasionalMoneyFlowItemDelegate, d => d.Name = name, l => l.Model);
                if (newMoneyFlowItemResult.IsValid)
                    _occasionalItems.ModifyCollection(col => col.Add(newMoneyFlowItemResult.Result));
                return newMoneyFlowItemResult.IsValid ? ActionResult.ValidResult : ActionResult.GetErrorResult(newMoneyFlowItemResult);
            });
        }

        private ActionResult<IEnumerable<StableMoneyFlowItemModel>> EnumerateStableMoneyFlowItems()
        {
            return UpdateHelper.EnumerateModels(EnumerateStableMoneyFlowItemsDelegate, l => l.Model);
        }

        private ActionResult<IEnumerable<OccasionalMoneyFlowItemModel>> EnumerateOccasionalMoneyFlowItems()
        {
            return UpdateHelper.EnumerateModels(EnumerateOccasionalMoneyFlowItemsDelegate, l => l.Model);
        }

        protected virtual void OnNewStableMoneyFlowItemNameChanged()
        {
            EventHandler handler = NewStableMoneyFlowItemNameChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        protected virtual void OnNewOccasionalMoneyFlowItemNameChanged()
        {
            EventHandler handler = NewOccasionalMoneyFlowItemNameChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }
    }
}
